﻿using System;
using System.Linq;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Serialization;
using Chaunce.Web.Core.FluentValidation;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection.Extensions;
using App.Metrics.Filtering;
using App.Metrics;
using App.Metrics.Formatters.Json;
using App.Metrics.Reporting.InfluxDB;
using Microsoft.AspNetCore.Mvc;
using Chaunce.Web.Core.Filters;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Chaunce.Web.Core.Providers;
using Chaunce.FrameWork.NetCore.SysCore.Configuration;
using Chaunce.FrameWork.NetCore.EasyRedis;
using Chaunce.FrameWork.NetCore.SysCore.Infrastructure;
using Chaunce.FrameWork.NetCore.Chaunce.Logging;

namespace Chaunce.Web.Core.Infrastructure.Extensions
{
    /// <summary>
    /// Represents extensions of IServiceCollection
    /// </summary>
    public static class ServiceCollectionExtensions
    {
        /// <summary>
        /// Add services to the application and configure service provider
        /// </summary>
        /// <param name="services">Collection of service descriptors</param>
        /// <param name="configuration">Configuration root of the application</param>
        /// <returns>Configured service provider</returns>
        public static IServiceProvider ConfigureApplicationServices(this IServiceCollection services, IConfigurationRoot configuration)
        {
            //add NopConfig configuration parameters
            services.ConfigureStartupConfig<ChaunceConfig>(configuration.GetSection("Chaunce"));
            //add hosting configuration parameters
            services.ConfigureStartupConfig<HostingConfig>(configuration.GetSection("Hosting"));
            //redis连接配置信息
            services.ConfigureStartupConfig<RedisCacheOptions>(configuration.GetSection("RedisCacheOptions"));

            //add accessor to HttpContext
            services.AddHttpContextAccessor();
            //日志初始化配置
            ConfigureSerilogConfig(services, configuration);

            //create, initialize and configure the engine
            var engine = EngineContext.Create();
            engine.Initialize(services);
            var serviceProvider = engine.ConfigureServices(services, configuration);

            return serviceProvider;
        }

        /// <summary>
        /// Create, bind and register as service the specified configuration parameters 
        /// </summary>
        /// <typeparam name="TConfig">Configuration parameters</typeparam>
        /// <param name="services">Collection of service descriptors</param>
        /// <param name="configuration">Set of key/value application configuration properties</param>
        /// <returns>Instance of configuration parameters</returns>
        public static TConfig ConfigureStartupConfig<TConfig>(this IServiceCollection services, IConfiguration configuration) where TConfig : class, new()
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services));

            if (configuration == null)
                throw new ArgumentNullException(nameof(configuration));

            //create instance of config
            var config = new TConfig();

            //bind it to the appropriate section of configuration
            configuration.Bind(config);

            //and register it as a service
            services.AddSingleton(config);

            return config;
        }

        /// <summary>
        /// 配置日志组件初始化
        /// </summary>
        /// <param name="configuration"></param>
        public static void ConfigureSerilogConfig(this IServiceCollection services, IConfiguration configuration)
        {
            //初始化日志组件
            ChaunceSerilogSetting setting = new ChaunceSerilogSetting();
            setting.Configuration = configuration;//使用appsettings.json文件初始化serilog
            setting.ApplicationName = services.GetChaunceConfig()?.ApplicationName;
            setting.Sinks = new SerilogSink[] { SerilogSink.File };
            ChaunceSerilog.InitSerilog(setting);
        }

        /// <summary>
        /// Register HttpContextAccessor
        /// </summary>
        /// <param name="services">Collection of service descriptors</param>
        public static void AddHttpContextAccessor(this IServiceCollection services)
        {
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }




        /// <summary>
        /// Add and configure MVC for the application
        /// </summary>
        /// <param name="services">Collection of service descriptors</param>
        /// <returns>A builder for configuring MVC services</returns>
        public static IMvcBuilder AddChaunceMvc(this IServiceCollection services)
        {
            var ChaunceConfig = services.GetChaunceConfig();

            //支持IIS
            services.Configure<IISOptions>(options =>
            {
                options.ForwardClientCertificate = false;
            });
            //替换控制器所有者
            services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
            //add basic MVC feature
            var mvcBuilder = services.AddMvc(config =>
            {
                config.Filters.Add(new ChaunceExceptionFilter());//自定义全局异常过滤器
                config.Filters.Add(new CustomAuthorizeFilter(new AuthorizationPolicyBuilder()
                    .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                    .RequireAuthenticatedUser().Build())); //controller统一认证 重写401 403返回值

                //同类型的过滤按添加先后顺序执行,第一个最先执行
                config.Filters.Add(new ViewModelStateActionFilter());//viewmodel数据合法性验证过滤器
                config.Filters.Add(new PermissionActionFilter());//用户权限验证过滤器
                config.Filters.Add(new BenchmarkActionFilter());//接口性能监控过滤器

            });
            //mvc binder对象转换支持空字符串.如果传入空字符串为转成空字符串，默认会转成null
            mvcBuilder.AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add(new CustomMetadataProvider()));

            //MVC now serializes JSON with camel case names by default, use this code to avoid it
            mvcBuilder.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());

            //add fluent validation
            mvcBuilder.AddFluentValidation(configuration =>
            {
                configuration.ValidatorFactoryType = typeof(ChaunceValidatorFactory);
                //implicit/automatic validation of child properties 复合对象是否验证
                configuration.ImplicitlyValidateChildProperties = true;
            });

            //支持跨域访问
            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
            });

            if (ChaunceConfig.AMPEnabled)
            {
                mvcBuilder.AddMetrics();
            }
            return mvcBuilder;
        }

        /// <summary>
        /// EF 数据库连接注册
        /// </summary>
        /// <param name="services"></param>
        public static void AddChaunceDbContext(this IServiceCollection services)
        {

        }

        /// <summary>
        /// 新增EFCore性能监控
        /// </summary>
        /// <param name="services"></param>
        public static void AddMiniProfilerEF(this IServiceCollection services)
        {
            var config = services.GetChaunceConfig();
            if (!config.MiniProfilerEnabled) return;
            // services.AddMiniProfiler().AddEntityFramework();
        }

        /// <summary>
        /// 新增redis缓存 注入
        /// </summary>
        /// <param name="services"></param>
        public static void AddRedisCache(this IServiceCollection services)
        {
            var config = services.GetChaunceConfig();
            if (!config.RedisCacheEnabled) return;

            //redis 连接配置文件

            var redisOptions = services.BuildServiceProvider().GetRequiredService<RedisCacheOptions>();

            services.AddChaunceRedisCache(options =>
           {
               options = redisOptions;
           });

        }

        /// <summary>
        /// 获取配置信息
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static ChaunceConfig GetChaunceConfig(this IServiceCollection services)
        {
            return services.BuildServiceProvider().GetRequiredService<ChaunceConfig>();
        }
    }
}